package com.nice.common.memory;

import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;

class BufferBlock {

    static final int CHUNK_SIZE = 1024;
    /**
     * 当前block从pool的那个位置开始
     */
    volatile int startIndex;

    /**
     * 当前block剩余字节
     */
    AtomicInteger remainChunk;

    /**
     * 当前block的容量
     */
    final int capacity;

    /**
     * 关连的pool
     */
    final byte[] pool;

    /**
     * 所有的chunk
     */
    private BufferChunck[] chunks;

    BufferBlock(byte[] pool, int capacity, int startIndex) {
        this.pool = pool;
        this.capacity = capacity;
        this.startIndex = startIndex;
        this.initChunk();
    }

    private void initChunk() {
        int j = 0;
        int i = startIndex;
        int remain = capacity % CHUNK_SIZE;
        int chunkCount = capacity / CHUNK_SIZE;
        int realCount = (remain > 0) ? chunkCount + 1 : chunkCount;

        chunks = new BufferChunck[realCount];
        while (j < realCount) {
            chunks[j++] = new BufferChunck(CHUNK_SIZE, i, true);
            i += CHUNK_SIZE;
        }
        if (remain > 0) {
            chunks[j++] = new BufferChunck(remain, i, true);
        }
        remainChunk = new AtomicInteger(realCount);
        System.out.println(String.format("spit[%s]bytes to[%s]chunk", capacity, realCount));
    }

    // initArgs{byte[] buf,int mark, int pos, int lim, int cap,int off}
    synchronized ByteBuffer createBuffer(int size, Constructor<?> constructor, Object[] initArgs) {
        try {
            if (size <= CHUNK_SIZE) {
                for (BufferChunck ck : chunks) {
                    if (ck.free) {
                        initArgs[5] = ck.start;
                        initArgs[4] = size;
                        initArgs[3] = size;
                        initArgs[0] = pool;
                        ck.free = false;
                        remainChunk.decrementAndGet();
                        return (ByteBuffer) constructor.newInstance(initArgs);
                    }
                }
                throw new PoolBufferException(String.format("find chunk error:[Chunkreamin:%s,allocate:%s]",
                        remainChunk.get(), size));
            }
            int chunkRequired = size / CHUNK_SIZE;
            if (size % CHUNK_SIZE > 0)
                chunkRequired++;
            int tmp = chunkRequired;
            // 扫描一个合适的连续chunk块
            for (int j = 0; j < chunks.length; j++) {
                if (chunks[j].free) {
                    tmp--;
                } else {
                    tmp = chunkRequired;
                }
                if (tmp == 0) {
                    int k = j;
                    while (chunkRequired-- > 0) {
                        BufferChunck bufferChunck = chunks[k--];
                        remainChunk.getAndDecrement();
                        bufferChunck.free = false;
                    }
                    initArgs[5] = chunks[k + 1].start;
                    initArgs[4] = size;
                    initArgs[3] = size;
                    initArgs[0] = pool;
                    return (ByteBuffer) constructor.newInstance(initArgs);
                }
            }
            throw new PoolBufferException(String.format("find chunk error:[poolreamin:%s,allocate:%s]",
                    remainChunk.get(), size));
        } catch (Exception e) {
            e.printStackTrace();
            throw new PoolBufferException("fail to create buffer:" + e.getMessage(), e.getCause());
        }
    }

    synchronized void recovery(ByteBuffer buf, int blockIndex) {
        int arrayOffset = buf.arrayOffset();
        int arrryOffsetEnd = arrayOffset + buf.capacity();
        int chunkIndex = (arrayOffset - startIndex) / CHUNK_SIZE;
        while (chunkIndex < chunks.length) {
            BufferChunck ck = chunks[chunkIndex++];
            if (ck.start > arrryOffsetEnd)
                break;
            remainChunk.getAndIncrement();
            ck.free = true;
        }
    }

    synchronized boolean canAllocate(int size) {
        return (size / CHUNK_SIZE) >= remainChunk.get();
    }

    /**
     * 
     * block里最小的数据单元
     * 
     * @author coder_czp-2015年4月24日
     */
    private class BufferChunck {

        volatile boolean free;
        final int capacity;
        final int start;

        BufferChunck(int capacity, int start, boolean free) {
            this.capacity = capacity;
            this.start = start;
            this.free = free;
        }

    }

}
